package com.sinevil.tcpTrans.utils.fileTrans.fileDownload;

import com.sinevil.tcpTrans.utils.fileTrans.FileTranser;
import com.sinevil.tcpTrans.utils.fileTrans.entity.FilePacketRequestInfo;
import com.sinevil.tcpTrans.utils.fileTrans.entity.FileTask;
import lombok.Getter;
import lombok.SneakyThrows;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.SocketChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;


/**
 * @Classname FileDownloadTask
 * @Description TODO
 * @Version 1.0.0
 * @Date 2025/1/14 14:42
 * @Created by sinevil
 */
public class FileDownloadTask extends FileTask implements Serializable {
    /**
     * 文件名
     */
    @Getter
    protected String name;
    /**
     * 临时文件名
     */
    protected String tempName;
    /**
     * 下载文件的保存路径，字符串形式
     */
    private String sPath;
    /**
     * 临时文件的保存路径，字符串形式
     */
    protected String sTempPath;

    /**
     * 保存路径
     * 既是文件的保存路径，也是文件的临时保存路径
     * 临时保存路径为文件名+uid.temp
     * 下载完成后，将临时文件重命名为文件名
     */
    protected Path path;
    @Getter
    protected long fileSize;
    /**
     * 需要切片的数量
     */
    protected int sliceCount;
    /**
     * 一共需要传输的数据包数量
     */
    private final int packetsNum;

    /**
     * 已写入大小
     */
    protected long writtenByte;

    /**
     * 间隔intervalTime的下载速度
     * 即：n B/s  = speed/intervalTime
     */
    protected AtomicInteger speedCount = new AtomicInteger(0);
    /**
     * 下载速度 KB
     */
    protected int speed = 0;
    /**
     * 是否为大文件
     */
    public boolean isBig;
    /**
     * 标志文件传输状态
     * 1 创建任务，未开始
     * 2 任务进行中
     * 3 任务失败
     * 4 任务完成
     */
    public int mark;

    protected RandomAccessFile raf;
    protected FileChannel channel;

    protected ByteMapManager byteMapManager;
    /**
     * 开始验证MD5的时间
     */
    public long checkStarTime;
    /**
     * 上传端返回的文件MD5
     */
    public String uploadMD5;
    public String MD5 = null;

    /**
     * 已请求的数据包列表
     * (packetIndex,time)
     */
    protected final Map<Long,Long> hasRequestMap =  new ConcurrentHashMap<>();

    /**
     * 创建一个文件下载任务
     * @param path 文件路径
     * @param uid 任务唯一标识
     * @param size 文件大小
     * @throws FileNotFoundException 如果文件不存在则抛出
     * @throws IOException  If some other I/O error occurs
     */
    public FileDownloadTask(SocketChannel socketChannel, Path path, String uid, long size) throws IOException {
        super(socketChannel,uid);
        this.name = path.getFileName().toString();
        this.sPath = path.toString();

        this.path = getTempPath(path, uid);
        this.tempName = this.path.getFileName().toString();
        this.sTempPath = this.path.toString();
        this.fileSize = size;
        // 先将保存路径文件名设为文件名+uid.temp
        this.tempName = this.path.toString();
        this.sliceCount = (int) Math.ceil((double) fileSize / FileTranser.SLICE_SIZE);
        this.packetsNum = (int) Math.ceil((double) fileSize / FileTranser.PACKET_SIZE);
        this.writtenByte = 0;
        this.isBig = sliceCount > 1;
        this.mark = 1;
        this.raf = new RandomAccessFile(this.path.toFile(),"rwd");
        this.channel = this.raf.getChannel();

        Path byteMapPath = path.getParent().resolve("temp").resolve(uid+".temp");
        // TODO 对于未开始下载的任务，不应该创建位映射状态文件
        this.byteMapManager = new ByteMapManager(byteMapPath,this.packetsNum);
    }

    /**
     * 获取指定数量数据包请求信息
     * @param n 数据包数量
     * @return 数据包请求信息列表
     */
    public FilePacketRequestInfo[] getPacketRequestInfo(int n){
        FilePacketRequestInfo[] packetRequestInfos = new FilePacketRequestInfo[n];
        List<Long> unreceivedPackets = this.byteMapManager.checkUnreceivedPackets(n);
        // 构建数据包请求列表
        for(int i=0;i<unreceivedPackets.size();i++){
            long packetOffset = unreceivedPackets.get(i);
            if(packetOffset < this.fileSize){
                packetRequestInfos[i] = new FilePacketRequestInfo(this.uid, packetOffset,FileTranser.PACKET_SIZE);
            }else if((this.fileSize - packetOffset) < FileTranser.PACKET_SIZE ){
                packetRequestInfos[i] = new FilePacketRequestInfo(this.uid, packetOffset, (int) (fileSize % FileTranser.PACKET_SIZE));
            }
        }
        return packetRequestInfos;
    }


    /**
     * 检查此请求是否存在
     * @param requestOffset 此请求数据包的偏移值
     * @return 请求是否存在
     */
    public boolean containsRequest(long requestOffset){
        return this.hasRequestMap.containsKey(requestOffset);
    }

    /**
     * 写入数据包，更新位映射状态，并移除对应数据包请求
     * @param offset 偏移量
     * @param size 数据包大小
     * @param byteBuffer 数据包数据
     */
    public void writePacket(long offset, int size, ByteBuffer byteBuffer){
        try {
            byteBuffer.flip();
            byteBuffer.limit(size);
            if(containsRequest(offset)){
//                System.out.println("已收到数据包："+offset);
//                synchronized(this.hasRequestMap){
                    // 将收到的数据包写入指定偏移值处
                    int written = this.channel.write(byteBuffer, offset);
                    if (size == written) {
                        // 在映射中标记数据包已完成
                        this.byteMapManager.acceptPacket(offset);
                        // 更新已接收的字节数
                        this.writtenByte += written;
                        speedCount.getAndAdd(written);
//                        System.out.println("当前speedCount：" + speedCount.get());
//                        System.out.printf("已写入数据包：offset(%d),size(%d)\n",offset,size);
                    }else{
                        System.out.println("写入数据包失败："+offset);
                    }
//                }
            }
        } catch (IOException e) {
            System.out.println("写入数据包时异常失败："+offset);
            throw new RuntimeException(e);
        }
    }

    /**
     * 检查是否所有数据包都已收到
     * @return 是否所有数据包都已收到
     */
    public boolean isComplete(){
        if (this.writtenByte >= this.fileSize) {
            if (this.byteMapManager.isComplete()) {
                this.mark = 4;
                return true;
            }else{
                if(this.byteMapManager.checkISCompleteBatch()){
                    this.mark = 4;
                    return true;
                }
            }
        }
        return false;
    }

    public String getMD5(){
        if (this.MD5 == null){
            try {
                if (this.channel == null) {
                    return null;
                }

                MessageDigest MD5Util = MessageDigest.getInstance("MD5");
                ByteBuffer buffer = ByteBuffer.allocate(FileTranser.MB);
                channel.position(0);
                while (this.channel.read(buffer) != -1) {
                    buffer.flip();
                    MD5Util.update(buffer);
                    buffer.clear();
                }

                byte[] digest = MD5Util.digest();
                this.MD5 = HexFormat.of().formatHex(digest);
                return this.MD5;
            } catch (IOException | NoSuchAlgorithmException e) {
                throw new RuntimeException("Failed to compute MD5 hash", e);
            }
        }
        return this.MD5;
    }

    /**
     * 任务正常完成
     */
    @SneakyThrows
    public void completeDownload(){
        if (this.mark == 4) {
            // 1. 先释放所有资源
            releaseResources();
            Path finalTargetPath = getNonConflictingPath(this.sPath);
            try {
                // 2. 确保文件未被占用时重命名
                Files.move(this.path, finalTargetPath);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private Path getNonConflictingPath(String originalPath) {
        Path targetPath = Path.of(originalPath);
        // 如果原始路径不存在，则返回原始路径
        if (!Files.exists(targetPath)) {
            return targetPath;
        }
        String fileName = targetPath.getFileName().toString();
        int dotIndex = fileName.lastIndexOf('.');
        String baseName = dotIndex > 0 ? fileName.substring(0, dotIndex) : fileName;
        String extension = dotIndex > 0 ? fileName.substring(dotIndex) : "";
        int counter = 1;
        Path newPath;
        do {
            String newFileName = baseName + "(" + counter + ")" + extension;
            newPath = targetPath.resolveSibling(newFileName);
            counter++;
        } while (Files.exists(newPath));
        return newPath;
    }

    /**
     * 任务非正常完成
     */
    public void closeTask(){
        // 关闭文件相关资源
        releaseResources();
    }

    /**
     * 释放读当前任务资源
     */
    private void releaseResources(){

        try {
            // 关闭文件通道和随机访问文件
            if (this.channel != null) {
                this.channel.close();
                this.channel = null;
            }
            if (this.raf != null) {
                this.raf.close();
                this.raf = null;
            }
            // 释放 ByteMapManager 资源
            if (this.byteMapManager != null) {
                this.byteMapManager.releaseResources();
                this.byteMapManager = null;
            }

            // 根据任务状态删除文件
            if (this.mark != 4) {
                // 任务非正常完成，删除临时文件
                if (this.path != null && Files.exists(this.path)) {
                    Files.delete(this.path);
                }
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 获取临时文件的保存路径
     * @param originalPath 原始文件的路径
     * @param uid 文件的唯一标识符
     * @return 临时文件的路径
     */
    private Path getTempPath(Path originalPath, String uid) {
        String originalFileName = originalPath.getFileName().toString();
        String tempFileName = originalFileName + "_" + uid + ".temp";
        Path parent = originalPath.getParent();
        return parent.resolve(tempFileName);
    }

    // 自定义的序列化方法
    @Serial
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();

    }

    // 自定义的反序列化方法
    @Serial
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        // 调用默认的反序列化方法
        // 自定义反序列化逻辑
    }

    /**
     * 使用bit映射每个数据包的状态
     */
    class BitMapManager implements Serializable {
        @Serial
        private static final long serialVersionUID = 1L;
        private FileChannel channel;
        private MappedByteBuffer buffer;
        private Path mapFilePath;
        private int numPackets;
        private int byteNum;
        private long completedIndex = 0L;
        private boolean isComplete = false;

        /**
         * 使用通道创建对象
         * 此位映射状态文件通道必须为可读写模式
         * @param channel 位映射状态文件通道
         * @param numPackets 数据包数量，即位映射状态文件大小
         * @throws IOException
         */
        public BitMapManager(FileChannel channel, int numPackets) throws IOException {
            this.numPackets = numPackets;
            this.buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, this.numPackets);
        }

        /**
         * 使用位映射状态文件地址创建对象
         * @param mapFilePath 位映射状态文件地址
         * @param numPackets 数据包数量，即位映射状态文件大小
         * @throws FileNotFoundException 如果模式为“r”，但给定的文件对象不表示现有的常规文件，或者模式以“rw”开头，但给定的文件对象不表示现有的可写常规文件，并且不能创建同名的新常规文件，或者在打开或创建文件时发生其他错误
         * @throws IOException 如果发生其他IO错误
         */
        public BitMapManager(Path mapFilePath, int numPackets) throws IOException {
            this.mapFilePath = mapFilePath;
            this.numPackets = numPackets;

            File file = mapFilePath.toFile();
            // 检查文件是否存在
            if (file.exists()) {
                if (file.length() == numPackets / 8) {
                    // 文件存在且大小符合预期
                    this.channel = new RandomAccessFile(file, "rw").getChannel();
                    this.buffer = this.channel.map(FileChannel.MapMode.READ_WRITE, 0, this.numPackets);
                } else {
                    // 文件存在但大小不符合预期，覆盖写为映射状态文件，并初始化为0
                    file.delete(); // 删除旧文件
                    file.createNewFile(); // 创建新文件
                    this.channel = new RandomAccessFile(file, "rw").getChannel();
                    this.buffer = this.channel.map(FileChannel.MapMode.READ_WRITE, 0, this.numPackets);
                    initializeBuffer(); // 初始化缓冲区为0
                }
            } else {
                // 文件不存在，创建并初始化位映射状态文件
                file.createNewFile();
                this.channel = new RandomAccessFile(file, "rw").getChannel();
                this.buffer = this.channel.map(FileChannel.MapMode.READ_WRITE, 0, this.numPackets);
                initializeBuffer(); // 初始化缓冲区为0
            }
        }

        // 获取指定位置 bit 的值
        public static boolean getBitValue(MappedByteBuffer buffer, int bitIndex) {
            int byteIndex = bitIndex / 8;
            int bitOffset = bitIndex % 8;
            byte currentByte = buffer.get(byteIndex);
            return ((currentByte >> bitOffset) & 1) == 1;
        }
        // 将指定位置的 bit 设置为 1
        public static void setBitToOne(MappedByteBuffer buffer, int bitIndex) {
            int byteIndex = bitIndex / 8;
            int bitOffset = bitIndex % 8;
            byte currentByte = buffer.get(byteIndex);
            byte newByte = (byte) (currentByte | (1 << bitOffset));
            buffer.put(byteIndex, newByte);
        }
        // 将指定位置的 bit 清除为 0
        public static void setBitToZero(MappedByteBuffer buffer, int bitIndex) {
            int byteIndex = bitIndex / 8;
            int bitOffset = bitIndex % 8;
            byte currentByte = buffer.get(byteIndex);
            byte newByte = (byte) (currentByte & ~(1 << bitOffset));
            buffer.put(byteIndex, newByte);
        }
        // 翻转指定位置的 bit
        public static void toggleBit(MappedByteBuffer buffer, int bitIndex) {
            int byteIndex = bitIndex / 8;
            int bitOffset = bitIndex % 8;
            byte currentByte = buffer.get(byteIndex);
            byte newByte = (byte) (currentByte ^ (1 << bitOffset));
            buffer.put(byteIndex, newByte);
        }

        /**
         * 初始化映射文件
         */
        private void initializeBuffer() {
            this.byteNum = (int) Math.ceil((double) this.numPackets / 8);
            ByteBuffer tempBuffer = ByteBuffer.allocate(byteNum);
            for (int i = 0; i < byteNum; i++) {
                tempBuffer.put((byte) 1);
            }
            tempBuffer.flip();
            this.buffer.put(tempBuffer);
            this.buffer.force();
        }


        /**
         * 根据 数据包索引 更新映射值
         * @param index 数据包索引
         */
        public synchronized void updateBitMapState(int index) {

            System.out.println(getBitValue(this.buffer, index));
            setBitToOne(this.buffer, index);
            System.out.println(getBitValue(this.buffer, index));

            System.out.println("操作之前是否为 1: " + getBitValue(buffer, 14));
            // 设置为 1
            setBitToOne(buffer, 14);
            System.out.println("是否为 1: " + getBitValue(buffer, 14));
            // 清除为 0
            setBitToZero(buffer, 14);
            System.out.println("是否为 1: " + getBitValue(buffer, 14));
            // 翻转
            toggleBit(buffer, 14);
            System.out.println("是否为 1: " + getBitValue(buffer, 14));
        }


        /**
         * 获取指定数量未收到的数据包
         *
         * @param n 数量
         * @return 未收到的数据包偏移值列表
         */
        public List<Long> checkUnreceivedPackets(int n) {
            List<Long> unreceived = new ArrayList<>();
            boolean foundZero = false;
            int startByteIndex = (int) (completedIndex / 8);
            if (!hasRequestMap.isEmpty()){
                // 重试超时的请求
                long currentTime = System.currentTimeMillis();
                long timeOut = currentTime - FileTranser.TIME_OUT;
                synchronized (hasRequestMap) {
                    for (Map.Entry<Long, Long> entry : hasRequestMap.entrySet()) {
                        if (entry.getValue() < timeOut) {
                            unreceived.add(entry.getKey());
                            hasRequestMap.replace(entry.getKey(), currentTime);
                        }
                    }
                }
            }
            // 遍历每个字节
            for (int i = startByteIndex; i < byteNum; i++) {
                byte currentByte = buffer.get(i);
                // 遍历字节的每个 bit
                for (int j = 0; j < 8; j++) {
                    if ((currentByte & (1 << j)) == 0) {
                        int packetIndex = i * 8 + j;
                        if (packetIndex < numPackets) {
                            // 首次发现 0 状态位，更新 completedIndex
                            if (!foundZero) {
                                if (completedIndex > 0)
                                    completedIndex = packetIndex - 1;
                                else
                                    completedIndex = 0;
                                foundZero = true;
                            }
                            long offset = ((long) packetIndex) * FileTranser.PACKET_SIZE;
                            synchronized (hasRequestMap) {
                                if (!hasRequestMap.containsKey(offset)) {
                                    unreceived.add(offset);
                                    hasRequestMap.put(offset, System.currentTimeMillis());
                                }
                            }
                            if (unreceived.size() >= n) {
                                return unreceived;
                            }
                        }
                    }
                }
            }
            return unreceived;
        }

        public boolean isComplete(){
            if (isComplete)
                return true;
            // 遍历每个字节
            for (int i = 0; i < byteNum; i++) {
                byte currentByte = buffer.get(i);
                // 遍历字节的每个bit
                for (int j = 0; j < 8; j++) {
                    if ((currentByte & (1 << j)) == 0) {
                        int packetIndex = i * 8 + j;
                        if (packetIndex < numPackets) {
                            isComplete = false;
                            return false;
                        }
                    }
                }
            }
            isComplete = true;
            return true;
        }

        @Serial
        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            in.defaultReadObject();
            this.channel = new RandomAccessFile(mapFilePath.toFile(), "rw").getChannel();
            this.buffer = this.channel.map(FileChannel.MapMode.READ_WRITE, 0, this.numPackets);
        }

        @SneakyThrows
        public void releaseResources(){
            unmap(this.buffer);
            this.channel.close();
        }

        /**
         * 释放MappedByteBuffer
         * 由于MappedByteBufferd的unmap方法并不对外开发，索引只能曲线救国
         * @param var0 MappedByteBuffer对象
         */
        private void unmap(MappedByteBuffer var0) {
            try {
                Field cleanerField = var0.getClass().getDeclaredField("cleaner");
                cleanerField.setAccessible(true);
                Object cleaner = cleanerField.get(var0);
                Method cleanMethod = cleaner.getClass().getMethod("clean");
                cleanMethod.invoke(cleaner);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }

    }


    /**
     * 使用byte映射每个数据包的状态
     */
     class ByteMapManager implements Serializable {
        @Serial
        private static final long serialVersionUID = 1L;
        private static final int MAX_SEGMENT_SIZE = 1024*1024;
        private static final byte TRUE = (byte) 1;
        private static final byte FALSE = (byte) 0;
        private final int numPackets;
        private final int maxIndex;
        /**
         *  针对MappedByteBuffer buffer 的锁
         */
        private final ReentrantLock lock = new ReentrantLock();

        private Path mapFilePath;
        // 映射文件的File对象
        private File tempFile;
        private RandomAccessFile raf;
        private FileChannel channel;
        private MappedByteBuffer buffer;
        private FileLock exclusive_lock;
        // 当前映射的起始索引和结束索引（绝对索引）
        private int currentSegmentStart;
        private int currentSegmentEnd;
        private int currentSegmentSize;
        private int updateIndex;
        private int completedIndex = -1;
        private boolean isComplete = false;

        public ByteMapManager(FileChannel channel, int numPackets) throws IOException {
            this.numPackets = numPackets;
            this.maxIndex = numPackets - 1;
            this.channel = channel;
            this.exclusive_lock = channel.tryLock();
            initializeBuffer();
            updateSegment();
        }

        public ByteMapManager(Path mapFilePath, int numPackets) throws IOException {
            this.mapFilePath = mapFilePath;
            this.numPackets = numPackets;
            this.maxIndex = numPackets - 1;
            this.tempFile = mapFilePath.toFile();
            if (this.tempFile.exists()) {
                if (this.tempFile.length() == numPackets) {
                    this.raf = new RandomAccessFile(this.tempFile, "rw");
                    this.channel = this.raf.getChannel();
                    this.exclusive_lock = this.channel.tryLock();
                    updateSegment();
                } else {
//                    System.out.println("文件损坏，重新创建");
                    this.tempFile.delete();
                    this.tempFile.createNewFile();
                    this.raf = new RandomAccessFile(this.tempFile, "rw");
                    this.channel = this.raf.getChannel();
                    this.exclusive_lock = this.channel.tryLock();
                    this.buffer = this.channel.map(FileChannel.MapMode.READ_WRITE, 0, this.currentSegmentSize);
                    initializeBuffer();

                    updateSegment();
                }

            } else {
                this.tempFile.createNewFile();
                this.raf = new RandomAccessFile(this.tempFile, "rw");
                this.channel = raf.getChannel();
                this.exclusive_lock = this.channel.tryLock();

                initializeBuffer();

                updateSegment();
            }
        }

        /**
         * 初始化映射文件
         */
        private void initializeBuffer() {
            int numSegments = (int) Math.ceil((float) numPackets / MAX_SEGMENT_SIZE);
            byte[] falseBytes = new byte[MAX_SEGMENT_SIZE];
            for (int segmentIndex = 0; segmentIndex < numSegments; segmentIndex++) {
                updateSegment();
                this.buffer.put(falseBytes, 0, currentSegmentSize-1);
                this.buffer.force();
                this.completedIndex+=currentSegmentSize;
            }
            this.completedIndex = -1;
            this.currentSegmentStart = 0;
            this.currentSegmentEnd = 0;
            this.buffer = null;
        }

        /**
         * 根据 数据包偏移值 更新映射值
         * 并从已请求列表中移除
         * @param offset 偏移值
         */
        public void acceptPacket(long offset) {
            hasRequestMap.remove(offset);
            // 计算绝对索引
            int index = (int) (offset / FileTranser.PACKET_SIZE);
            updateByteMapState(index);
        }

        /**
         * 根据 数据包索引 更新映射值
         * @param index 绝对索引
         */
        public synchronized void updateByteMapState(int index) {
            // index不可能大于等于numPackets
            if (index >= this.numPackets) {
                return;
            }
            lock.lock();
            try {
                if (index <= currentSegmentEnd && index >= currentSegmentStart) {
                    int relativeIndex = index - currentSegmentStart;
                    buffer.put(relativeIndex, TRUE);
                } else if (index > this.currentSegmentEnd && this.completedIndex == this.currentSegmentEnd) {
//                    System.out.println("更新映射 " + index + " 时 更新段");
                    updateSegment();
                    int relativeIndex = index - currentSegmentStart;
                    buffer.put(relativeIndex, TRUE);
                }
            } finally {
                lock.unlock();
            }
        }

        /**
         * 获取指定偏移值的数据包映射 的值
         * @param offset 偏移值
         * @return 是否已经接收
         */
        public boolean getByteByOffset(long offset) {
            int index = (int) (offset/FileTranser.PACKET_SIZE);
            return getByteByAbsoluteIndex(index);
        }

        /**
         * 获取指定相对索引处的映射值
         * @param index 绝对索引
         * @return 是否已经接收
         */
        private boolean getByteByRelativeIndex(int index) {
            lock.lock();
            try {
                // 相对索引必定小于当前段的大小，即一般不会出现所有越界情况
                if(index < currentSegmentSize) {
                    return buffer.get(index) == TRUE;
                }
                return false;
            } catch (NullPointerException e) {
                // 打印当前completedIndex
                System.out.println("completedIndex: " + completedIndex);
                updateByteMapState(index);
                // 相对索引必定小于当前段的大小，即一般不会出现所有越界情况
                if(index < currentSegmentSize) {
                    return buffer.get(index) == TRUE;
                }
                return false;
            } finally {
                lock.unlock();
            }
        }

        /**
         * 获取指定绝对索引处的映射值
         * @param index 绝对索引
         * @return 是否已经接收
         */
        private boolean getByteByAbsoluteIndex(int index) {
            lock.lock();
            try {
                int relativeIndex = index - currentSegmentStart;
                return getByteByRelativeIndex(relativeIndex);
            } finally {
                lock.unlock();
            }
        }

        /**
         * 更新当前映射段
         */
        private void updateSegment() {
            // 当已完成索引小于预期更新索引或不在当前段范围时不更新
//            if (completedIndex < this.updateIndex | completedIndex < currentSegmentStart | completedIndex > currentSegmentEnd) return;
            lock.lock();
            try {
                // 如果currentSegmentEnd >= this.maxIndex 则表示此段已是最后一个段，不再需要更新段
                if (currentSegmentEnd < this.maxIndex) {
                    this.currentSegmentStart = Math.max(this.completedIndex, 0);
                    this.currentSegmentSize = Math.min(numPackets - currentSegmentStart, MAX_SEGMENT_SIZE);
                    this.currentSegmentEnd = currentSegmentStart + currentSegmentSize-1;
                    if (this.currentSegmentEnd >= maxIndex) {
                        // 当前段已是最后一段，不再需要更新段
                        this.updateIndex = numPackets;
                    }else {
                        // 设置段更新位置位当前段的四分之三处，当complereIndex>=this.updateIndex时更新段
                        this.updateIndex = currentSegmentStart + (currentSegmentSize * 3) / 4;
                    }
                    this.buffer = channel.map(FileChannel.MapMode.READ_WRITE, currentSegmentStart, currentSegmentSize);

                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }

        /**
         * 获取指定数量未收到的数据包
         * 包括重发超时请求
         *
         * @param n 数量
         * @return 未收到的数据包偏移值列表
         */
        public List<Long> checkUnreceivedPackets(int n) {
            List<Long> unreceived;
            try {
                this.lock.lock();

                unreceived = new ArrayList<>();
                if (buffer == null) {
                    this.updateSegment();
                }

                if (!hasRequestMap.isEmpty()) {
                    // 重试超时请求
                    long currentTime = System.currentTimeMillis();
                    long timeOut = currentTime - FileTranser.TIME_OUT;
                    synchronized (hasRequestMap) {
                        for (Map.Entry<Long, Long> entry : hasRequestMap.entrySet()) {
                            if (entry.getValue() < timeOut) {
                                unreceived.add(entry.getKey());
                                hasRequestMap.replace(entry.getKey(), currentTime);
                                if (unreceived.size() >= n) return unreceived;
                            }
                        }
                    }
                }

                boolean foundZero = false;
                int startByteIndex = Math.max(0, this.completedIndex);
                // 遍历当前段，获取未收到的数据包
                for (int i = startByteIndex; i <= this.currentSegmentEnd; i++) {
                    if (!getByteByAbsoluteIndex(i)) {
                        // 首次发现 0 状态位，更新 completedIndex
                        if (!foundZero) {
                            if (i > 0) this.completedIndex = i - 1;
                            else this.completedIndex = -1;
                            foundZero = true;
                            if (this.completedIndex >= this.updateIndex) {
                                //                                System.out.printf("check时更新段：当前段为%d -- %d ，", this.currentSegmentStart,this.currentSegmentEnd);
                                this.updateSegment();
                                //                                System.out.println("更新后段为"+this.currentSegmentStart+" -- "+this.currentSegmentEnd + "   " + System.currentTimeMillis());
                            }
                        }
                        long offset = (long) i * FileTranser.PACKET_SIZE;
                        synchronized (hasRequestMap) {
                            if (!hasRequestMap.containsKey(offset)) {
                                unreceived.add(offset);
                                hasRequestMap.put(offset, System.currentTimeMillis());
                                if (unreceived.size() >= n) return unreceived;
                            }
                        }
                    }

                }
            } finally {
                this.lock.unlock();
            }

            return unreceived;
        }

        /**
         * 用于初始化后检查初始化是否成功
         * @return 映射初始化状态是否全为false，如果全为false则返回false，反之返回true
         */
        public boolean checkISFalse() {
            if (this.isComplete) return true;
            this.lock.lock();
            try {
                this.completedIndex = -1;
                this.currentSegmentStart = 0;
                this.currentSegmentEnd = 0;
                this.updateSegment();
                for (int i = 0; i <= this.maxIndex; i++) {
                    if (!getByteByAbsoluteIndex(i)) {
                        this.completedIndex = i;
                        if (this.completedIndex >= this.updateIndex) {
                            this.updateSegment();
                        }
                    } else {
                        return true;
                    }
                }
                return false;
            } finally {
                this.completedIndex = -1;
                this.currentSegmentStart = 0;
                this.currentSegmentEnd = 0;
                this.lock.unlock();
            }
        }

        /**
         * 检查所有数据包是否都已收到
         * @return 是否已完成
         */
        public boolean checkISComplete() {
            try {
                if (this.isComplete) return true;
                this.lock.lock();
                this.completedIndex = -1;
                this.currentSegmentStart = 0;
                this.currentSegmentEnd = 0;
                this.updateSegment();
                for (int i = 0; i <= this.maxIndex; i++) {
                    if (getByteByAbsoluteIndex(i)) {
//                        System.out.println("已检查：" + i + " 为true");
                        this.completedIndex = i;
                        if (this.completedIndex >= this.updateIndex) {
//                            System.out.println("检查时更新段为：" + this.currentSegmentStart+" -- "+this.currentSegmentEnd);
                            this.updateSegment();
                        }
                    } else {
//                        System.out.println("已检查：" + i + " 为false");
                        this.isComplete = false;
                        return false;
                    }
                }
                this.isComplete = true;
                return true;
            } finally {
                this.completedIndex = -1;
                this.currentSegmentStart = 0;
                this.currentSegmentEnd = 0;
                this.lock.unlock();
            }
        }

        /**
         * 批量检查所有数据包是否都已收到
         * @return 是否已完成
         */
        public boolean checkISCompleteBatch() {
            try {
                if (this.isComplete) return true;
                this.lock.lock();
                this.completedIndex = -1;
                this.currentSegmentStart = 0;
                this.currentSegmentEnd = 0;
                this.updateSegment();

                int segmentNum = (int) Math.ceil((float) this.numPackets / MAX_SEGMENT_SIZE);
                byte[] bytes;
                if (segmentNum > 1) {
                    bytes = new byte[MAX_SEGMENT_SIZE];
                }else {
                    bytes = new byte[this.numPackets];
                }
                Arrays.fill(bytes, TRUE);
                for (int i = 0; i <= segmentNum; i++) {
                    // 批量读取映射文件的内容
                    try {
                        this.buffer.get(bytes);
                    }catch (BufferUnderflowException ignored){}

                    for (int j = 0; j < this.currentSegmentSize; j++) {
                        if (bytes[j] != TRUE) {
                            return false;
                        }
                    }
                    this.completedIndex += this.currentSegmentSize;
//                    System.out.println("检查时更新段为：" + this.currentSegmentStart+" -- "+this.currentSegmentEnd);
                    updateSegment();
                }
                this.isComplete = true;
                return true;
            } finally {
                this.completedIndex = -1;
                this.currentSegmentStart = 0;
                this.currentSegmentEnd = 0;
                this.lock.unlock();
            }
        }

        public boolean isComplete() {
            return this.isComplete;
        }

        @Serial
        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            in.defaultReadObject();
            this.channel = new RandomAccessFile(mapFilePath.toFile(), "rw").getChannel();
            this.currentSegmentStart = 0;
            this.currentSegmentSize = Math.min(numPackets, MAX_SEGMENT_SIZE);
            this.buffer = this.channel.map(FileChannel.MapMode.READ_WRITE, 0, this.currentSegmentSize);
        }


        public void releaseResources() {
            lock.lock();
            try {
                // 1. 强制刷新（可选）
                if (buffer != null) {
                    buffer.force();
                }
                buffer = null;

                // 2. 关闭通道（自动释放锁和 MappedByteBuffer）
                closeQuietly(channel);
                channel = null;

                // 3. 关闭文件
                closeQuietly(raf);
                raf = null;

                System.gc();

                // 4. 删除临时文件
                deleteTempFile();
            } finally {
                lock.unlock();
            }
        }

        private void closeQuietly(AutoCloseable resource) {
            if (resource != null) {
                try {
                    resource.close();
                } catch (IOException e) {
                    System.err.println("关闭资源失败: " + e.getMessage());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }

        private void deleteTempFile() {
            if (tempFile != null && tempFile.exists()) {
                int attempts = 0;
                while (attempts < 10) {

                    System.out.printf("尝试 %d: %s \n", attempts + 1, tempFile.getName());

                    try {
                        Files.delete(tempFile.toPath());
                        System.out.println("删除成功: " + tempFile.getAbsolutePath());
                        return;
                    } catch (IOException e) {
                        System.err.println("删除异常: " + e.getMessage());
                    }


                    attempts++;
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException ignored) {}
                }
                System.err.println("删除失败: " + tempFile.getAbsolutePath());
            }
        }

        /**
         * 检查文件是否被占用
         * @param file
         * @return
         */
        private static boolean isFileInUse(File file) {
            if (!file.exists()) return false;
            try (RandomAccessFile raf = new RandomAccessFile(file, "rw");
                 FileChannel channel = raf.getChannel()) {
                return channel.tryLock() == null;
            } catch (Exception e) {
                return true;
            }
        }
    }


}